package com.esfak47.mediafetcher.plugin.pornhub.service.impl;

import com.esfak47.common.PageResult;
import com.esfak47.common.lang.Inject;
import com.esfak47.common.logger.Logger;
import com.esfak47.common.logger.LoggerFactory;
import com.esfak47.mediafetcher.core.domain.FetchAble;
import com.esfak47.mediafetcher.core.domain.Media;
import com.esfak47.mediafetcher.core.domain.Thumbnail;
import com.esfak47.mediafetcher.core.domain.VideoExtInfo;
import com.esfak47.mediafetcher.core.service.CrawlerService;
import com.esfak47.mediafetcher.core.service.MediaProvider;
import com.esfak47.mediafetcher.core.service.ProvideQuery;
import com.esfak47.mediafetcher.plugin.pornhub.PornHubAPI;
import com.esfak47.mediafetcher.plugin.pornhub.dto.PornHubSearchResult;
import com.esfak47.mediafetcher.plugin.pornhub.dto.PornTubeVideo;
import retrofit2.Retrofit;
import retrofit2.adapter.java8.Java8CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author tony
 */

public class PornHubMediaProvider implements MediaProvider {
    public final static String TYPE_PORN_HUB = "pornhub";

    private static final Logger log = LoggerFactory.getLogger(PornHubMediaProvider.class);
    private final PornHubAPI pornHubAPI;
    @Inject
    private CrawlerService crawlerService;
    @Inject
    private Executor ioExecutor;
    private Function<PornTubeVideo, Media>
        pornTubeVideoMediaFunction = video -> Media.builder().webpageUrl(
        video.getLocation())
        .rating(video.getRating())
        .id(video.getId().toString())
        .title(video.getTitle())
        .thumbnail(video.getUrlThumbnail())
        .viewCount(video.getViewCount())
        .duration(video.getDuration())
        .extractorKey("PornHub")
        .build();

    public PornHubMediaProvider() {
        Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.pornhub.com/")
            .addConverterFactory(GsonConverterFactory.create())

            .addCallAdapterFactory(Java8CallAdapterFactory.create())
            .build();
        this.pornHubAPI = retrofit.create(PornHubAPI.class);

    }

    public static Media videoExtInfoConverter(VideoExtInfo info) {
        FetchAble fetchAble = ((FetchAble)info.getFetchAble());
        System.out.println(fetchAble);
        return Media
            .builder()
            .categories(info.getCategories())
            .commentCount(info.getCommentCount())
            .duration(info.getDuration())
            .thumbnail(info.getThumbnail())
            .thumbnails(info.getThumbnails().stream().map(Thumbnail::getUrl).collect(Collectors.toList()))
            .ext(info.getExt())
            .extractorKey(info.getExtractorKey())
            .fullTitle(info.getFullTitle())
            .title(info.getTitle())
            .formatId(info.getFormatId())
            .tags(info.getTags())
            .fileName(info.getFilename())
            .url(info.getUrl())
            .viewCount(info.getViewCount())
            .rating(info.getLikeCount())
            .webpageUrl(info.getWebpageUrl())
            .build();
    }

    @Override
    public PageResult<Media> provide(ProvideQuery provideQuery) {
        PageResult<Media> emptyPaginationDTO = PageResult.createEmptyPageResult(
            provideQuery.getOffset() / provideQuery.getMaxCount(), provideQuery.getMaxCount());
        PornHubAPI.OrderType orderType = getOrderType(provideQuery);

        CompletableFuture<PornHubSearchResult> jap =
            pornHubAPI.search(orderType, provideQuery.getMaxCount(), provideQuery.getOffset(),
                provideQuery.getKeyWords());

        if (provideQuery.isCrawler()) {
            Optional<List<Media>> media = doCrawler(jap);
            try {
                PornHubSearchResult result = jap.get(provideQuery.getTimeOutSeconds(), TimeUnit.SECONDS);
                return PageResult.createPageResult(provideQuery.getOffset() / provideQuery.getMaxCount()
                    , provideQuery.getMaxCount(), result.getCount(), media.orElse(Collections.emptyList()));
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                e.printStackTrace();
                return emptyPaginationDTO;
            }

        } else {
            try {
                PornHubSearchResult result = jap.get(provideQuery.getTimeOutSeconds(), TimeUnit.SECONDS);
                if (result == null || result.getVideos() == null) {
                    return emptyPaginationDTO;
                }
                List<Media> collect = result.getVideos().stream().map(pornTubeVideoMediaFunction).collect(
                    Collectors.toList());
                return PageResult.createPageResult(provideQuery.getOffset() / provideQuery.getMaxCount(),
                    provideQuery.getMaxCount(), result.getCount(), collect);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                log.error("get info failed", e);
                return emptyPaginationDTO;
            }
        }

    }

    private PornHubAPI.OrderType getOrderType(ProvideQuery provideQuery) {
        PornHubAPI.OrderType orderType = null;
        if (provideQuery.getOrderType() == null) {
            provideQuery.setOrderType(OrderType.nc.name());
        }
        try {
            orderType = PornHubAPI.OrderType.valueOf(provideQuery.getOrderType());
        } catch (IllegalArgumentException e) {
            orderType = PornHubAPI.OrderType.mv;
        }
        return orderType;
    }

    private Optional<List<Media>> doCrawler(CompletableFuture<PornHubSearchResult> jap) {
        CompletableFuture<List<Media>> listCompletableFuture = jap.thenApplyAsync(
            pornHubSearchResult ->
                pornHubSearchResult.getVideos()
                    .stream()
                    .filter(PornTubeVideo::canAccessMedia)
                    .map(pornTubeVideo -> crawlerService.getVideoExtInfoAsync(pornTubeVideo))
                    .map(CompletableFuture::join)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .collect(Collectors.toList()), ioExecutor);
        try {
            List<Media> medias = listCompletableFuture.get();

            return Optional.of(medias);

        } catch (InterruptedException | ExecutionException e) {
            log.error("fetch media failed ", e);
        }
        return Optional.empty();
    }

}
